package ptij.ADSL.de.ATUC;

import ptolemy.kernel.*;
import ptolemy.kernel.util.*;
import ptolemy.data.*;
import ptolemy.data.expr.*;
import ptolemy.data.type.*;
import ptolemy.actor.*;
import ptolemy.math.Complex;

import ptij.ADSL.common.FastFECDataFrame;

//////////////////////////////////////////////////////////////////////////
//// Map
/**

@author Ivan Jeukens
*/
public class Map extends TypedAtomicActor {
                
    public Map(CompositeEntity container, String name) throws 
            NameDuplicationException, IllegalActionException {
        super(container, name);

	fast = new TypedIOPort(this, "fast", true, false);
	fast.setTypeEquals(BaseType.OBJECT);
	
	inter = new TypedIOPort(this, "inter", true, false);
	inter.setTypeEquals(BaseType.OBJECT);

        AS0Bytes = new Parameter(this, "AS0Bytes", new IntToken(8));
	LS0Bytes = new Parameter(this, "LS0Bytes", new IntToken(8));
	
	AS0Map = new Parameter(this, "AS0Map", new BooleanToken(false));
	LS0Map = new Parameter(this, "LS0Map", new BooleanToken(false));
	
	S = new Parameter(this, "S", new IntToken(1));

        parityFast = new Parameter(this, "parityFast", new IntToken(2));
	parityInter = new Parameter(this, "parityInter", new IntToken(2));
        
        outputMode = new Parameter(this, "outputMode", new IntToken(1));
        _outputMode = 1;
        
        _createOutputInterface();	
    }

    ///////////////////////////////////////////////////////////////////
    ////                     ports and parameters                  ////

    public TypedIOPort fast;
    public TypedIOPort inter;    
    public TypedIOPort subChannel[];

    public Parameter AS0Bytes;
    public Parameter LS0Bytes;
    public Parameter AS0Map;
    public Parameter LS0Map;    
    public Parameter S;
    public Parameter parityFast;
    public Parameter parityInter;
    public Parameter outputMode;

    ///////////////////////////////////////////////////////////////////
    ////                         public methods                    ////

     public void attributeChanged(Attribute attribute)
            throws IllegalActionException {
        if(attribute == AS0Bytes) {
	    IntToken it = (IntToken) AS0Bytes.getToken();
	    _AS0Bytes = it.intValue();            
        }
        else
        if(attribute == LS0Bytes) {
            IntToken it = (IntToken) LS0Bytes.getToken();
            _LS0Bytes = it.intValue();            
        }
        else
        if(attribute == AS0Map) {
            BooleanToken bt = (BooleanToken) AS0Map.getToken();
	    _AS0Map = bt.booleanValue();
        }
        else
        if(attribute == LS0Map) {
            BooleanToken bt = (BooleanToken) LS0Map.getToken();
	    _LS0Map = bt.booleanValue();   
        }
        else
        if(attribute == S) {
 	    IntToken it = (IntToken) S.getToken();
	    _S = it.intValue();           
        }
        else
        if(attribute == outputMode) {
            /*IntToken it = (IntToken) outputMode.getToken();
            _outputMode = it.intValue();
            try {
                _createOutputInterface();
            }
            catch(NameDuplicationException ex) {
                ex.printStackTrace(System.out);
            } */           
        }
	else {
            super.attributeChanged(attribute);
        }        

        _kkFast = _kkInter = 1;
	if(!_AS0Map) {
	    _kkFast += _AS0Bytes;
	}
	else {
	    _kkInter += _AS0Bytes;
	}
	
	if(!_LS0Map) {
	    _kkFast += _LS0Bytes;
	}
	else {
	    _kkInter += _LS0Bytes;
	}
	
	if(!_AS0Map /* || !_AS1Map || ... */) {
 	    _kkFast += 1;
	}	
	if(!_AS0Map || !_LS0Map) {
	    _kkFast += 1;
	}
	
	if(_AS0Map) {
 	    _kkInter += 1;
	}	
	if(_AS0Map || _LS0Map) {
	    _kkInter += 1;
	}
        _kkInter *= _S;
        
        
        if(attribute == parityFast) {
	    IntToken it = (IntToken) parityFast.getToken();
	    _nnFast = _kkFast + it.intValue();                    
        }
        else
        if(attribute == parityInter) {            
            IntToken it = (IntToken) parityInter.getToken();
            _nnInter = _kkInter + it.intValue();            
        }                        			                        
    }
    
    public void initialize() throws IllegalActionException {
        super.initialize();

        _checkParameters();
        
	_bitTable = new int[255];
	_subChannelTable = new int[255];
	_gainTable = new Complex[255];
	
	int tot = (_nnFast + _nnInter) * 8;
	for(int i = 0;i < 255;i++) {
	    _subChannelTable[i] = i;
	    if(i == 63) {
	        _bitTable[i] = 0;
		_gainTable[i] = new Complex(1,1);
	        continue;
	    }
	    _gainTable[i] = new Complex(1, 1);

            if(tot > 4) {
	        _bitTable[i] = 5;
	        tot -= 5;
	    }
	    else
	    if(tot > 0) {
	        _bitTable[i] = tot;
	        tot = 0;
	    }
	    else {
	        _bitTable[i] = 0;
	    }
	}

        _dataBuffer = new int[_nnInter + _nnFast];
	_dataCounter = 0;
	_func = 0;	
    }
    
    public void fire() throws IllegalActionException {    
	switch(_func) {
	    case 0 : {
	        if(fast.hasToken(0)) {
		    ObjectToken ot = (ObjectToken) fast.get(0);
	            FastFECDataFrame fastf = (FastFECDataFrame) ot.getValue();

	            int [] data = fastf.getAsArray();
	            for(int i = 0;i < data.length;i++) {
	                _dataBuffer[_dataCounter] = data[i];
	                _dataCounter++;
	            }
		    _func = 1;
		}
	    } break;
	    
	    case 1 : {
	        if(inter.hasToken(0)) {
                    ObjectToken  ot = (ObjectToken) inter.get(0);
	            int [] data = (int []) ot.getValue();
	            for(int j = 0;j < data.length;j++) {
	                _dataBuffer[_dataCounter] = data[j];
	                _dataCounter++;
	            }
	            _func = 2;
		}
	    } break;
	    
	    case 2 : {
	        if(_outputMode == 0) {
	            int k = 0;
	            int w = 0;
	            for(int i = 0;i < 255;i++) {
	                if(i == 63) {
	                    subChannel[63].send(0, 
			        new DoubleToken(_gainTable[63].real));
		            subChannel[63].send(0, 
			        new DoubleToken(_gainTable[63].imag));
		            continue;
	                }
	                int b = 0;
	                for(int j = 0;j < _bitTable[i];j++) {
                            b |= (((_dataBuffer[w] >> k) & 0x01) << j);
		            k++;
		            if(k == 8) {
		                k = 0;
		                w++;
		            }
	                }
	                Complex Z = _constellation(b, _bitTable[i]);
	                subChannel[_subChannelTable[i]].send(0,
			    new DoubleToken(Z.real));
	                subChannel[_subChannelTable[i]].send(0,
			    new DoubleToken(Z.imag));
		     }
		 }
		 else {
	             int k = 0;
	             int w = 0;
		     double symbol[] = new double[510];
		     for(int i = 0;i < 255;i++) {
		         if(i == 63) {
			     symbol[2*i] = _gainTable[63].real;
			     symbol[2*i + 1] = _gainTable[63].imag;
			 }
                         int b = 0;
	                 for(int j = 0;j < _bitTable[i];j++) {
                             b |= (((_dataBuffer[w] >> k) & 0x01) << j);
		             k++;
		             if(k == 8) {
		                 k = 0;
		                 w++;
		             }
	                 }
	                 Complex Z = _constellation(b, _bitTable[i]);
			 symbol[2*i] = Z.real;
			 symbol[2*i + 1] = Z.imag;
	             }
		     subChannel[0].send(0, new ObjectToken(symbol));
		 }

	        _dataCounter = 0;
	        _func = 0;
	    } break;
	}
    }

    ///////////////////////////////////////////////////////////////////
    ////                         private methods                   ////
    
    private Complex _constellation(int b, int nb) {    	
	if(nb == 0) {
	    return new Complex(0, 0);
	}
	else
	if(nb == 1) {
	    if((b & 0x01) == 1) {
	        return new Complex(1,1);
	    }
	    else {
	        return new Complex(-1, 1);
	    }
	}
	else
	if(nb % 2 == 0) {
	    int X = 1, Y = 1, k = 1;
	    for(int i = 0;i < nb;i++) {
	        if(i % 2 == 0) {
		    if(i == nb - 2) {
		        if(((b >> i) & 0x01) == 1) {
			    for(int j = k;j < 32;j++) Y |= (0x01 << j);
			}
		    }
		    else {
		        Y |= (((b >> i) & 0x01) << k);
		    }
		}
		else {
		    if(i == nb - 1) {		          
		        if(((b >> i) & 0x01) == 1) {
			    for(int j = k;j < 32;j++) X |= (0x01 << j);
			}
		    }
		    else {
			X |= (((b >> i) & 0x01) << k);
	            }
		    k++;
		}
	    }
	    return new Complex(X, Y);
	}
	else
	if(nb == 3) {
	    switch(b) {
	        case 0: return new Complex(1, 1);
		case 1: return new Complex(1, -1);
		case 2: return new Complex(-1, 1);
		case 3: return new Complex(-1, -1);
		case 4: return new Complex(-3, 1);
		case 5: return new Complex(1, 3);
		case 6: return new Complex(-1, -3);
		case 7: return new Complex(3, -1);
	    }
	}
	else {
	    int X = 1, Y = 1, i, k = 1;
	    for(i = 0;i < nb - 3;i++) {
	        if(i % 2 == 0) {
		    Y |= (((b >> i) & 0x01) << k);
		}
		else {
	            X |= (((b >> i) & 0x01) << k);
		    k++;
		}
	    }
	    int tmp = 0;
	    int l = 0;
	    for(int j = i - 2;j < nb;j++) {
	        tmp |= ((b >> j) & 0x01) << l;
		l++;	    
	    }            
	    Y |= (_lastY[tmp] << k);
	    X |= (_lastX[tmp] << k);
	    k++;

            if(_signX[tmp] == 1) {
                for(int j = k;j < 32;j++) X |= (0x01 << j);
	    }
	    if(_signY[tmp] == 1) {
                for(int j = k;j < 32;j++) Y |= (0x01 << j);
	    }

	    return new Complex(X, Y);	
	}	
	return new Complex(0, 0);
    }

    private void _checkParameters() throws IllegalActionException {
        if(_AS0Bytes < 0) {
	    throw new IllegalActionException(this, "AS0 Bytes < 0");	
	}
	
	if(_LS0Bytes < 0) {
	    throw new IllegalActionException(this, "LS0 Bytes < 0");
	}
	
	if(_S != 1 && _S != 2 && _S != 4 && _S != 8 && _S != 16) {
	    throw new IllegalActionException(this, "\nInvalid value(" +
	        _S + ") for parameter S");
	}	

	if(_nnFast > 255) {
	    throw new IllegalActionException(this, "Maximum Fast codeword"+
	        " size is 255");
	}
	int par = _nnFast - _kkFast;
	if((par < 0) || (par > 16) || (par % 2 != 0)) {
	    throw new IllegalActionException(this, "Invalid number(" + par +
	        ") of fast parity bytes");
	}	       

	if(_nnInter > 255) {
	    throw new IllegalActionException(this, "Maximum Interleaved codeword"+
	        " size is 255");
	}
        par = _nnInter - _kkInter;
	if((par < 0) || (par > 16) || (par % 2 != 0)) {
	    throw new IllegalActionException(this, "Invalid number(" + par +
	        ") of interleaved parity bytes");
	}
        if(par % _S != 0) {
	    throw new IllegalActionException(this, "Interleaved parity " +
	        " bytes should be an integer multiple of S!");
	}    
    }

    private void _createOutputInterface() throws IllegalActionException, 
            NameDuplicationException {
	if(_outputMode == 0) {
	    subChannel = new TypedIOPort[255];
	    for(int i = 0;i < 255;i++) {
	        subChannel[i] = new TypedIOPort(this, "subChannel_" + i,
	            false, true);
                subChannel[i].setTypeEquals(BaseType.DOUBLE);
	    }
	}
	else {
	    subChannel = new TypedIOPort[1];
	    subChannel[0] = new TypedIOPort(this, "subChannel0", false, true);
	    subChannel[0].setTypeEquals(BaseType.OBJECT);
	}
    }

    ///////////////////////////////////////////////////////////////////
    ////                         private variables                 ////
    
    private static final int _lastX[] = { 0, 0, 0, 0, 0, 0, 0, 0,
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1,
	1, 1, 1, 0, 0 };
	
    private static final int _lastY[] = { 0, 0, 0, 0, 1, 1, 1, 1,
        0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1,
	0, 1, 1, 1, 1 };
	
    private static final int _signX[] = { 0, 0, 0, 0, 0, 0, 0, 0,
        1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0,
	1, 1, 1, 1, 0, 0, 1, 1 };
    
    private static final int _signY[] = { 0, 0, 0, 0, 1, 1, 1, 1,
        0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1,
	0, 1, 1, 1, 1, 1 };

    private int _bitTable[];
    private int _subChannelTable[];
    private Complex _gainTable[];
    
    private int _dataBuffer[];
    private int _dataCounter;
    private int _func;
    
    private int _AS0Bytes;
    private int _LS0Bytes;
    private boolean _AS0Map;
    private boolean _LS0Map;
    private int _S;

    private int _nnFast, _kkFast;
    private int _nnInter, _kkInter;
    
    private int _outputMode;
}
